﻿namespace Gamelight.Graphics
{
	internal class IntermediateSurface
	{
		private static IntermediateSurface instance;

		static IntermediateSurface()
		{
			instance = new IntermediateSurface(1, 1);
		}

		private int counter;
		private int width;
		private int height;
		private int[,] pixels;
		private double[,] antialias;
		private int[] minYAffected;
		private int[] maxYAffected;
		private int[] columnAffected;
		private int minX;
		private int maxX;

		public int[,] Pixels { get { return this.pixels; } }
		public double[,] Antialias { get { return this.antialias; } }
		public int[] MinYAffected { get { return this.minYAffected; } }
		public int[] MaxYAffected { get { return this.maxYAffected; } }
		public int[] YAffectedAtAll { get { return this.columnAffected; } }
		public int Counter { get { return this.counter; } }
		public int AntiaAliasCounter { get { return this.counter + 1; } }


		private IntermediateSurface(int width, int height)
		{
			this.width = width;
			this.height = height;
			this.pixels = new int[width, height];
			this.antialias = new double[width, height];
			this.minYAffected = new int[width];
			this.maxYAffected = new int[width];
			this.columnAffected = new int[width];

			this.counter = 0;
		}

		public static IntermediateSurface InitializeTransaction(Point[][] multiplePointLists)
		{
			int outerListLength, innerListLength;
			int i, j;
			outerListLength = multiplePointLists.Length;
			Point[] pointList;
			Point p;
			int minX = 0;
			int minY = 0;
			int maxX = 0;
			int maxY = 0;
			int x, y;
			bool first = true;
			for (i = 0; i < outerListLength; ++i)
			{
				pointList = multiplePointLists[i];
				innerListLength = pointList.Length;
				for (j = 0; j < innerListLength; ++j)
				{
					p = pointList[j];
					x = p.X;
					y = p.Y;
					if (first)
					{
						minX = x;
						maxX = x;
						minY = y;
						maxY = y;
					}
					else
					{
						minX = x < minX ? x : minX;
						maxX = x > maxX ? x : maxX;
						minY = y < minY ? y : minY;
						maxY = y > maxY ? y : maxY;
					}
				}
			}

			return InitializeTransaction(minX - 1, minY - 1, maxX + 1, maxY + 1);
		}

		public static IntermediateSurface InitializeTransaction(Point[] points)
		{
			int pointsLength = points.Length;
			int xMin = points[0].X;
			int xMax = xMin;
			int yMin = points[0].Y;
			int yMax = yMin;
			int x, y;

			for (int i = 1; i < pointsLength; ++i)
			{
				x = points[i].X;
				y = points[i].Y;
				xMin = x < xMin ? x : xMin;
				xMax = x > xMax ? x : xMax;
				yMin = y < yMin ? y : yMin;
				yMax = y > yMax ? y : yMax;
			}

			return InitializeTransaction(xMin - 1, yMin - 1, xMax + 1, yMax + 1);
		}

		public static IntermediateSurface InitializeTransaction(int minX, int minY, int maxX, int maxY)
		{
			IntermediateSurface image = instance;

			maxX++;

			image.counter += 2;

			image.minX = minX < 0 ? 0 : minX;
			image.maxX = maxX;

			bool reinitialize = false;
			if (maxX > image.width)
			{
				image.width = maxX + 50;
				reinitialize = true;
			}
			if (maxY > image.height)
			{
				image.height = maxY + 50;
				reinitialize = true;
			}
			if (image.counter > 2000000000)
			{
				reinitialize = true;
			}

			if (reinitialize)
			{
				image.pixels = new int[image.width, image.height];
				image.antialias = new double[image.width, image.height];
				image.minYAffected = new int[image.width];
				image.maxYAffected = new int[image.width];
				image.columnAffected = new int[image.width];

				image.counter = 2;
			}

			return image;
		}

		public void BlitOntoImage(Image image, Color color)
		{
			int[] affectedAtAll = this.columnAffected;
			int[] minAffected = this.minYAffected;
			int[] maxAffected = this.maxYAffected;
			int counter = this.counter;
			int aacounter = this.counter + 1;
			int[,] pixels = this.pixels;
			double[,] antialias = this.antialias;

			int[] imagePixels = image.Pixels;
			int imageWidth = image.Width;
			int imageHeight = image.Height;

			int y, endY, endX, index;
			int colorValue = color.IntValue;
			byte colorRed = color.Red;
			byte colorGreen = color.Green;
			byte colorBlue = color.Blue;
			byte colorAlpha = color.Alpha;
			int presentRed, presentGreen, presentBlue, presentAlpha;
			int presentValue, finalA, oldWeight, fadedAlpha;

			endX = this.maxX < imageWidth ? this.maxX : imageWidth;

			for (int x = this.minX; x < endX; ++x)
			{
				if (affectedAtAll[x] == counter)
				{
					endY = maxAffected[x];
					if (endY >= imageHeight)
					{
						endY = imageHeight - 1;
					}

					for (y = minAffected[x]; y <= endY; ++y)
					{
						if (pixels[x, y] == counter)
						{
							index = x + y * imageWidth;
							imagePixels[index] = colorValue;
						}
						else if (pixels[x, y] == aacounter)
						{
							index = x + y * imageWidth;
							presentValue = imagePixels[index];

							presentAlpha = (presentValue >> 24) & 255;
							presentRed = (presentValue >> 16) & 255;
							presentGreen = (presentValue >> 8) & 255;
							presentBlue = presentValue & 255;
							fadedAlpha = (int)(colorAlpha * antialias[x, y]);

							finalA = 255 - (255 - fadedAlpha) * (255 - presentAlpha) / 255;
							oldWeight = presentAlpha * (255 - fadedAlpha) / 255;

							presentRed = (presentRed * oldWeight + colorRed * fadedAlpha) / (oldWeight + fadedAlpha);
							presentGreen = (presentGreen * oldWeight + colorGreen * fadedAlpha) / (oldWeight + fadedAlpha);
							presentBlue = (presentBlue * oldWeight + colorBlue * fadedAlpha) / (oldWeight + fadedAlpha);

							imagePixels[index] = (finalA << 24) + (presentRed << 16) + (presentGreen << 8) + presentBlue;
						}
					}
				}
			}
		}
	}
}
